package logic

import (
	"bufio"
	"common"
	"common/zapx"
	"errors"
	"fmt"
	"gateway/internal/pb"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"io"
	"path/filepath"
	"time"
)

// GetUriByKey 获取完整临时链接
func GetUriByKey(key string) string {
	conn, err := grpc.Dial(
		common.ConsulSerUrl("minio"),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		zapx.Logger.Error("连接minio服务失败", zap.Error(err))
		return key
	}
	defer conn.Close()
	client := pb.NewMinioServiceClient(conn)
	ctx, cancel := common.WithTimeoutContext(time.Second * 2)
	defer cancel()
	res, err := client.GetUrl(ctx, &pb.UrlRequest{Key: key})
	if err != nil {
		zapx.Logger.Error("查询链接失败", zap.Error(err))
		return key
	}
	return res.Uri
}

// UploadFile 上传文件到 minio
func UploadFile(fileName, contentType string, reader io.Reader) (*pb.UploadResponse, error) {
	//储存的 key 名称
	key := fmt.Sprintf("/upload/%s/%s%s",
		time.Now().Format("2006-01-02"),
		common.Md5IgnoreError([]byte(fileName)),
		filepath.Ext(fileName),
	)
	conn, err := grpc.Dial(
		common.ConsulSerUrl("minio"),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		zapx.Logger.Error("连接minio服务失败", zap.Error(err))
		return nil, errors.New("系统错误")
	}
	defer conn.Close()
	client := pb.NewMinioServiceClient(conn)
	ctx, cancel := common.WithTimeoutContext(time.Minute * 3)
	defer cancel()
	stream, err := client.Upload(ctx)
	bf := bufio.NewReader(reader)
	var allBytes []byte
	var len int
	for {
		chunk := make([]byte, 1024)
		n, err := bf.Read(chunk)
		if err != nil {
			if !errors.Is(err, io.EOF) {
				zapx.Logger.Error("读取数据失败", zap.Error(err))
				return nil, errors.New("读取数据失败")
			} else {
				res, err := stream.CloseAndRecv()
				if err != nil {
					zapx.Logger.Error("和minio grpc通讯失败", zap.Error(err))
					return nil, errors.New("上传文件失败,服务器出现错误")
				}
				return res, nil
			}
		}
		//发送数据
		if n > 0 {
			allBytes = append(allBytes, chunk...)
			len += n
			fileMd5 := common.Md5FileByBytes(allBytes[:len])
			if err := stream.Send(&pb.UploadRequest{File: &pb.File{
				FileName:    key,
				FileMd5:     fileMd5,
				Data:        chunk[:n],
				ContentType: &contentType,
			}}); err != nil {
				zapx.Logger.Error("和minio grpc通讯失败", zap.Error(err))
				return nil, errors.New("上传文件失败,服务器出现错误")
			}
		}
	}
}

// CheckMinioObjNameExists 检查对象是否存在
func CheckMinioObjNameExists(objName string) bool {
	conn, err := grpc.Dial(
		common.ConsulSerUrl("minio"),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		zapx.Logger.Error("[CheckMinioObjNameExists]连接minio服务失败", zap.Error(err))
		return false
	}
	defer conn.Close()
	minioClient := pb.NewMinioServiceClient(conn)
	ctx, cancel := common.WithTimeoutContext(time.Second * 2)
	defer cancel()
	_, err = minioClient.Exists(ctx, &pb.ExistsRequest{
		Key: objName,
	})
	if err != nil {
		return false
	}
	return true
}
